{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Energy system optimisation with oemof - how to collect and store results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Import necessary modules"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import pandas as pd\n",
    "from oemof.solph import (Sink, Source, Transformer, Bus, Flow, Model,\n",
    "                         EnergySystem, processing, views)\n",
    "\n",
    "import pickle"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Specify solver"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "solver = 'cbc'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create an energy system and optimize the dispatch at least costs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# initialize and provide data\n",
    "datetimeindex = pd.date_range('1/1/2016', periods=24*10, freq='H')\n",
    "energysystem = EnergySystem(timeindex=datetimeindex)\n",
    "filename = 'input_data.csv'\n",
    "filename = os.path.join(os.getcwd(), filename)\n",
    "data = pd.read_csv(filename, sep=\",\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create and add components to energysystem"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# resource buses\n",
    "bcoal = Bus(label='coal', balanced=False)\n",
    "bgas = Bus(label='gas', balanced=False)\n",
    "boil = Bus(label='oil', balanced=False)\n",
    "blig = Bus(label='lignite', balanced=False)\n",
    "\n",
    "# electricity and heat\n",
    "bel = Bus(label='bel')\n",
    "bth = Bus(label='bth')\n",
    "\n",
    "energysystem.add(bcoal, bgas, boil, blig, bel, bth)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/uwe/git-projects/oemof-solph/src/oemof/solph/network.py:184: SuspiciousUsageWarning: The `fixed` attribute is deprecated.\n",
      "If you have defined the `fix` attribute the flow variable will be fixed.\n",
      "The `fixed` attribute does not change anything.\n",
      "  warn(msg, debugging.SuspiciousUsageWarning)\n"
     ]
    }
   ],
   "source": [
    "# an excess and a shortage variable can help to avoid infeasible problems\n",
    "energysystem.add(Sink(label='excess_el', inputs={bel: Flow()}))\n",
    "# shortage_el = Source(label='shortage_el',\n",
    "#                      outputs={bel: Flow(variable_costs=200)})\n",
    "\n",
    "# sources\n",
    "energysystem.add(Source(label='wind', outputs={bel: Flow(\n",
    "    fix=data['wind'], nominal_value=66.3)}))\n",
    "\n",
    "energysystem.add(Source(label='pv', outputs={bel: Flow(\n",
    "    fix=data['pv'], nominal_value=65.3)}))\n",
    "\n",
    "# demands (electricity/heat)\n",
    "energysystem.add(Sink(label='demand_el', inputs={bel: Flow(\n",
    "    nominal_value=85, fix=data['demand_el'])}))\n",
    "\n",
    "energysystem.add(Sink(label='demand_th',\n",
    "                 inputs={bth: Flow(nominal_value=40,\n",
    "                                   fix=data['demand_th'],\n",
    "                                   fixed=True)}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# power plants\n",
    "energysystem.add(Transformer(\n",
    "    label='pp_coal',\n",
    "    inputs={bcoal: Flow()},\n",
    "    outputs={bel: Flow(nominal_value=20.2, variable_costs=25)},\n",
    "    conversion_factors={bel: 0.39}))\n",
    "\n",
    "energysystem.add(Transformer(\n",
    "    label='pp_lig',\n",
    "    inputs={blig: Flow()},\n",
    "    outputs={bel: Flow(nominal_value=11.8, variable_costs=19)},\n",
    "    conversion_factors={bel: 0.41}))\n",
    "\n",
    "energysystem.add(Transformer(\n",
    "    label='pp_gas',\n",
    "    inputs={bgas: Flow()},\n",
    "    outputs={bel: Flow(nominal_value=41, variable_costs=40)},\n",
    "    conversion_factors={bel: 0.50}))\n",
    "\n",
    "energysystem.add(Transformer(\n",
    "    label='pp_oil',\n",
    "    inputs={boil: Flow()},\n",
    "    outputs={bel: Flow(nominal_value=5, variable_costs=50)},\n",
    "    conversion_factors={bel: 0.28}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# combined heat and power plant (chp)\n",
    "energysystem.add(Transformer(\n",
    "    label='pp_chp',\n",
    "    inputs={bgas: Flow()},\n",
    "    outputs={bel: Flow(nominal_value=30, variable_costs=42),\n",
    "             bth: Flow(nominal_value=40)},\n",
    "    conversion_factors={bel: 0.3, bth: 0.4}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# heat pump with a coefficient of performance (COP) of 3\n",
    "b_heat_source = Bus(label='b_heat_source')\n",
    "energysystem.add(b_heat_source)\n",
    "\n",
    "energysystem.add(Source(label='heat_source', outputs={b_heat_source: Flow()}))\n",
    "\n",
    "cop = 3\n",
    "energysystem.add(Transformer(\n",
    "    label='heat_pump',\n",
    "    inputs={bel: Flow(),\n",
    "            b_heat_source: Flow()},\n",
    "    outputs={bth: Flow(nominal_value=10)},\n",
    "    conversion_factors={bel: 1/3, b_heat_source: (cop-1)/cop}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Optimization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Welcome to the CBC MILP Solver \n",
      "Version: 2.9.9 \n",
      "Build Date: Aug 21 2017 \n",
      "\n",
      "command line - /usr/bin/cbc -printingOptions all -import /tmp/tmpzvc245k8.pyomo.lp -stat=1 -solve -solu /tmp/tmpzvc245k8.pyomo.soln (default strategy 1)\n",
      "Option for printingOptions changed from normal to all\n",
      "Presolve 224 (-2417) rows, 1120 (-2721) columns and 1120 (-5361) elements\n",
      "Statistics for presolved model\n",
      "\n",
      "\n",
      "Problem has 224 rows, 1120 columns (1120 with objective) and 1120 elements\n",
      "There are 1120 singletons with objective \n",
      "Column breakdown:\n",
      "0 of type 0.0->inf, 997 of type 0.0->up, 0 of type lo->inf, \n",
      "123 of type lo->up, 0 of type free, 0 of type fixed, \n",
      "0 of type -inf->0.0, 0 of type -inf->up, 0 of type 0.0->1.0 \n",
      "Row breakdown:\n",
      "0 of type E 0.0, 0 of type E 1.0, 0 of type E -1.0, \n",
      "0 of type E other, 0 of type G 0.0, 0 of type G 1.0, \n",
      "224 of type G other, 0 of type L 0.0, 0 of type L 1.0, \n",
      "0 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other, \n",
      "0 of type Free \n",
      "Presolve 224 (-2417) rows, 1120 (-2721) columns and 1120 (-5361) elements\n",
      "Perturbing problem by 0.001 %% of 25 - largest nonzero change 3.7377626e-06 (%% 2.3037679e-05) - largest zero change 0\n",
      "0  Obj 142834.83 Primal inf 18711.432 (224)\n",
      "79  Obj 240801.65 Primal inf 11177.466 (145)\n",
      "158  Obj 331076.44 Primal inf 4177.8208 (66)\n",
      "224  Obj 383538.35\n",
      "Optimal - objective value 383538.3\n",
      "After Postsolve, objective 383538.3, infeasibilities - dual 0 (0), primal 0 (0)\n",
      "Optimal objective 383538.2988 - 224 iterations time 0.002, Presolve 0.00\n",
      "Total time (CPU seconds):       0.01   (Wallclock seconds):       0.02\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'Problem': [{'Name': 'unknown', 'Lower bound': 383538.2988, 'Upper bound': 383538.2988, 'Number of objectives': 1, 'Number of constraints': 2641, 'Number of variables': 3841, 'Number of nonzeros': 1120, 'Sense': 'minimize'}], 'Solver': [{'Status': 'ok', 'User time': -1.0, 'System time': 0.01, 'Wallclock time': 0.02, 'Termination condition': 'optimal', 'Termination message': 'Model was solved to optimality (subject to tolerances), and an optimal solution is available.', 'Statistics': {'Branch and bound': {'Number of bounded subproblems': None, 'Number of created subproblems': None}, 'Black box': {'Number of iterations': 224}}, 'Error rc': 0, 'Time': 0.05781960487365723}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# create optimization model based on energy_system\n",
    "optimization_model = Model(energysystem=energysystem)\n",
    "\n",
    "# solve problem\n",
    "optimization_model.solve(solver=solver,\n",
    "                         solve_kwargs={'tee': True, 'keepfiles': False})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Write results into energysystem.results object for later"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "energysystem.results['main'] = processing.results(optimization_model)\n",
    "energysystem.results['meta'] = processing.meta_results(optimization_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "string_results = views.convert_keys_to_strings(energysystem.results['main'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save results - Dump the energysystem (to ~/home/user/.oemof by default)\n",
    "Specify path and filename if you do not want to overwrite"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Attributes dumped to: /home/uwe/.oemof/dumps/es_dump.oemof'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "energysystem.dump(dpath=None, filename=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
